package com.world.cat.service.generate;

import com.world.cat.app.AppConstant;
import com.world.cat.dao.generate.AutoDao;
import com.world.common.generate.auto.*;
import com.world.common.pojo.Result;
import com.world.common.util.CollectionUtil;
import com.world.common.util.NumberUtil;
import com.world.core.dao.SqlBuildUtil;
import com.world.common.util.SysUtil;
import com.world.core.dao.Restrictions;
import com.world.core.dao.SimpleExpression;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.Id;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@Transactional
public class FieldService {
    //扫描所有需要自动生成页面的entity默认为entity包下面的所有类
    public static Map<String, String> entityMap = null;

    @javax.annotation.Resource
    private AutoDao autoDao;

    public static Map<String, List<QFieldInfo>> getFieldMap(Class clazz) {
        Map<String, List<QFieldInfo>> qFieldMap = new HashMap<>();
        Field[] fields = clazz.getDeclaredFields();
        List<QFieldInfo> qFieldInfoAll = new ArrayList<>();
        for (Field field : fields) {
            QField qField = field.getAnnotation(QField.class);
            if (qField != null) {
                Action[] actions = qField.actions();
                QFieldInfo fieldInfo = new QFieldInfo();
                fieldInfo.fromQField(field, qField);
                for (Action action : actions) {
                    List<QFieldInfo> qFields = qFieldMap.get(action.name());
                    if (qFields == null) {
                        qFields = new ArrayList<>();
                        qFieldMap.put(action.name(), qFields);
                    }
                    qFields.add(fieldInfo);
                }
                qFieldInfoAll.add(fieldInfo);
            }
        }
        qFieldMap.put("all", qFieldInfoAll);
        List<QFieldInfo> editFields = qFieldMap.get(Action.edit.name());
        if (editFields == null) {
            editFields = new ArrayList<>();
        }
        return qFieldMap;
    }

    public static Set<String> getClassActionInfo(Class clazz) {
        QClass qClass = (QClass) clazz.getAnnotation(QClass.class);
        if (qClass != null) {
            Set<String> classActions = new HashSet<>();
            for (QClassAction classAction : qClass.actions()) {
                classActions.add(classAction.name());
            }
            return classActions;
        } else {
            Set<String> classActions = new HashSet<>();
            for (QClassAction classAction : QClassAction.values()) {
                if (!"export".equals(classAction.name())) {
                    classActions.add(classAction.name());
                }
            }
            return classActions;
        }
    }

    public static List<String> getNotNullFields(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        List<String> qFieldInfoNotNull = new ArrayList<>();
        for (Field field : fields) {
            QField qField = field.getAnnotation(QField.class);
            if (qField != null) {
                QFieldInfo fieldInfo = new QFieldInfo();
                fieldInfo.fromQField(field, qField);
                if (!fieldInfo.getNullable()) {
                    qFieldInfoNotNull.add(fieldInfo.getName());
                }
            }
        }
        return qFieldInfoNotNull;
    }

    public static List<String> getNotNullEditFields(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        List<String> qFieldInfoNotNull = new ArrayList<>();
        for (Field field : fields) {
            QField qField = field.getAnnotation(QField.class);
            if (qField != null) {
                QFieldInfo fieldInfo = new QFieldInfo();
                fieldInfo.fromQField(field, qField);
                if (!fieldInfo.getNullable()) {
                    Action[] actions = fieldInfo.getActions();
                    if (actions != null) {
                        for (Action action : actions) {
                            if (Action.edit.equals(action)) {
                                qFieldInfoNotNull.add(fieldInfo.getName());
                                break;
                            }
                        }
                    }
                }
            }
        }
        return qFieldInfoNotNull;
    }

    private Map initEntityClass() {
        Map entityClass = new HashMap<String, String>();
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        String classpath = this.getClass().getResource("/").toString();
        try {
            Resource[] resources = resourcePatternResolver.getResources(AppConstant.entity_pattern);
            for (int i = 0; i < resources.length; i++) {
                String source = resources[i].getURL().toString();
                String str = source.substring(classpath.length(), source.length() - 6).replace("/", ".");
                String className = str.substring(str.lastIndexOf(".") + 1);
                entityClass.put(className, str);
            }
        } catch (Exception e) {
            return null;
        }

        return entityClass;
    }

    public Class obtainClass(String actionType) {
        if (actionType == null) {
            return null;
        }
        if (entityMap == null) {
            entityMap = initEntityClass();
        }
        if (StringUtils.isNotBlank(actionType)) {
            String className = entityMap.get(actionType.trim());
            try {
                return Class.forName(className);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public Object obtainEntity(String actionType) throws Exception {
        if (entityMap == null) {
            entityMap = initEntityClass();
        }
        if (StringUtils.isNotBlank(actionType)) {
            String className = entityMap.get(actionType.trim());
            try {
                Class clazz = Class.forName(className);
                return clazz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public Class obtainIdType(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            Annotation annotation = field.getAnnotation(Id.class);
            if (annotation != null) {
                return field.getType();
            }
        }
        return null;
    }

    public void del(Class clazz, String[] ids) {
        String findHql = "delete from " + clazz.getSimpleName() + " o where 1=1";
        List<SimpleExpression> expressionList = new ArrayList<SimpleExpression>();
        expressionList.add(Restrictions.in("id", Arrays.asList(ids), true));
        String delHql = findHql + SqlBuildUtil.buildWhereQuery(expressionList);
        autoDao.deleteByHql(delHql);
    }

    public String obtainIdField(Class clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            Annotation annotation = field.getAnnotation(Id.class);
            if (annotation != null) {
                return field.getName();
            }
        }
        return null;
    }

    public Page queryPageByHql(Class clazz, String params, Pageable pageable) throws Exception {
        String fromSql = "from " + clazz.getSimpleName() + " o where 1=1";
        List<SimpleExpression> expressionList = new ArrayList<>();
        if (StringUtils.isNotEmpty(params)) {
            JSONObject jsonObject = JSONObject.fromObject(params);
            for (Object o : jsonObject.entrySet()) {
                Map.Entry entry = (Map.Entry) o;
                if (entry.getValue() != null && StringUtils.isNotBlank(entry.getValue().toString())) {
                    String queryField = (String) entry.getKey();
                    if (queryField.endsWith("_start")) {
                        String field = queryField.replace("_start", "");
                        expressionList.add(Restrictions.gte(field, entry.getValue(), true));
                    } else if (queryField.endsWith("_end")) {
                        String field = queryField.replace("_end", "");
                        expressionList.add(Restrictions.lte(field, entry.getValue(), true));
                    } else {
                        if (StringUtils.isNotEmpty(queryField)) {
                            Field field = clazz.getDeclaredField(queryField);
                            if (field != null) {
                                QField qField = field.getAnnotation(QField.class);
                                if (qField != null) {
                                    if (QFieldQueryType.eq.equals(qField.queryType())) {
                                        expressionList.add(Restrictions.eq(queryField, entry.getValue(), true));
                                    } else if (QFieldQueryType.like.equals(qField.queryType())) {
                                        expressionList.add(Restrictions.like(queryField, (String) entry.getValue(), true));
                                    }
                                } else {
                                    expressionList.add(Restrictions.eq(queryField, entry.getValue(), true));
                                }
                            }
                        }
                    }
                }
            }
        }
        String whereHql = SqlBuildUtil.buildWhereQuery(expressionList);
        return autoDao.findByPageWithHql(fromSql + whereHql, pageable);
    }

    public List queryByHql(Class clazz, String params) throws Exception {
        String hql = "from " + clazz.getSimpleName() + " o where 1=1";
        List<SimpleExpression> expressionList = new ArrayList<SimpleExpression>();
        if (StringUtils.isNotEmpty(params)) {
            JSONObject jsonObject = JSONObject.fromObject(params);
            for (Object o : jsonObject.entrySet()) {
                Map.Entry entry = (Map.Entry) o;
                if (entry.getValue() != null && StringUtils.isNotBlank(entry.getValue().toString())) {
                    String queryField = (String) entry.getKey();
                    if (queryField.endsWith("_start")) {
                        String field = queryField.replace("_start", "");
                        expressionList.add(Restrictions.gte(field, entry.getValue(), true));
                    } else if (queryField.endsWith("_end")) {
                        String field = queryField.replace("_end", "");
                        expressionList.add(Restrictions.lte(field, entry.getValue(), true));
                    } else {
                        if (StringUtils.isNotEmpty(queryField)) {
                            Field field = clazz.getDeclaredField(queryField);
                            QField qField = field.getAnnotation(QField.class);
                            if (QFieldQueryType.eq.equals(qField.queryType())) {
                                expressionList.add(Restrictions.eq(queryField, entry.getValue(), true));
                            } else if (QFieldQueryType.like.equals(qField.queryType())) {
                                expressionList.add(Restrictions.like(queryField, (String) entry.getValue(), true));
                            }
                        }
                    }
                }
            }
        }
        String whereHql = SqlBuildUtil.buildWhereQuery(expressionList);
        return autoDao.findByHql(hql + whereHql);
    }

    public Result saveByHql(Class clazz, String params) throws Exception {
        Object obj = null;
        JSONObject jsonObject = JSONObject.fromObject(params);
        String id = (String) jsonObject.get("id");
        if (StringUtils.isEmpty(id)) {
            obj = clazz.newInstance();
        } else {
            obj = autoDao.findOne(clazz, id);
        }
//        Map<String, List<QFieldInfo>> qFieldMap = FieldService.getFieldMap(clazz);
        List<String> notNulls = getNotNullFields(clazz);
        for (Object o : jsonObject.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            if (notNulls.contains(entry.getKey().toString())) {
                if (SysUtil.isEmpty(entry.getValue().toString())) {
                    return new Result(false, "必填参数为空");
                }
                notNulls.remove(entry.getKey().toString());
            }
        }
        if (CollectionUtil.isNotEmpty(notNulls)) {
            return new Result(false, "必填参数页面未传递值");
        }
        for (Object o : jsonObject.entrySet()) {
            Map.Entry entry = (Map.Entry) o;
            String pageFiled = (String) entry.getKey();
            if (!"id".equals(pageFiled)) {
                Field field = clazz.getDeclaredField(pageFiled);
                Method method = clazz.getDeclaredMethod("set" + StringUtils.capitalize(pageFiled), field.getType());

                String value = (String) entry.getValue();
                if (String.class.equals(field.getType())) {
                    method.invoke(obj, value);
                } else if (Integer.class.equals(field.getType())) {
                    method.invoke(obj, NumberUtil.intValue(value));
                } else if (Long.class.equals(field.getType())) {
                    method.invoke(obj, NumberUtil.longValue(value));
                } else if (Double.class.equals(field.getType())) {
                    method.invoke(obj, NumberUtil.doubleValue(value));
                } else if (field.getType().isEnum()) {
                    Class enumclass = field.getType();
                    if (StringUtils.isNotEmpty(value)) {
                        method.invoke(obj, Enum.valueOf(enumclass, value));
                    }
                } else if (Date.class.equals(field.getType())) {
                    QField qField = field.getAnnotation(QField.class);
                    SimpleDateFormat format = new SimpleDateFormat(qField.format());
                    if (StringUtils.isNotEmpty(value)) {
                        method.invoke(obj, format.parse(value));
                    }
                }
            }
        }
        autoDao.saveObject(obj);
        return new Result(true);
    }

    public void save(Object obj) {
        autoDao.saveObject(obj);
    }

    public Object getEntity(Class clazz, String serializable) throws Exception {
        return autoDao.findOne(clazz, serializable);
    }
}
